#include "ParserEdit.h"

const string ParserEdit::regexInstructionAndArguments = "\\s*([a-zA-Z]+)\\s*(.*)";

const string ParserEdit::regexArgumentEditEnd = "\\s*([0-9]+)\\s+end\\s+(\\S.*?)\\s*";
const string ParserEdit::regexArgumentEditStart = "\\s*([0-9]+)\\s+start\\s+(\\S.*?)\\s*";
const string ParserEdit::regexArgumentEditRename = "\\s*([0-9]+)\\s+rename\\s+(\\S.*?)\\s*";

const string ParserEdit::regexInstructionEdit = "(edit)";

ParserEdit::ParserEdit(ParserBase* _nextParser) : ParserBase(_nextParser) {}

bool ParserEdit::tryParse(string userCommand, PARSED_COMMAND& parsedCommand){
	
	string instruction;
	string arguments;
	getInstructionAndArguments(userCommand, instruction, arguments);
	if (!checkInstruction(instruction)) {
		return false;
	}

	parseEdit(parsedCommand, arguments);

	return true;
}

bool ParserEdit::getInstructionAndArguments(const string& userCommand, 
										string& instruction, 
										string& arguments) {
	regex rgx(regexInstructionAndArguments);
	smatch matchResults;

	if (!regex_match(userCommand, matchResults, rgx)) {
		return false;
	}

	assert(matchResults.size() > ARGUMENTS_POS);
	instruction = matchResults[INSTRUCTION_POS];
	arguments = matchResults[ARGUMENTS_POS];
	

	return true;
}

// Checks if the instruction is "edit"
bool ParserEdit::checkInstruction(const string& instruction) {
	if (regex_match(instruction, regex(regexInstructionEdit,
									   tr1::regex_constants::icase))) {
		return true;
	}

	return false;
}

void ParserEdit::parseEdit(PARSED_COMMAND& parsedCommand,
					  const string& arguments) {	
	smatch matchResults;

	EditSubType editSubType = categorizeEditCommand(arguments, matchResults);

	switch (editSubType) {
	case EDIT_END:
		makeParsedCommandEditEnd(parsedCommand, matchResults);
		break;
	case EDIT_START:
		makeParsedCommandEditStart(parsedCommand, matchResults);
		break;
	case EDIT_NAME:
		makeParsedCommandEditRename(parsedCommand, matchResults);
		break;
	case INVALID_SUBTYPE:
		makeParsedCommandEditErrorSyntax(parsedCommand, matchResults);
		break;
	default:
		assert(false && "Unexpected invalid edit subtype!");
		break;
	}
}

ParserEdit::EditSubType ParserEdit::categorizeEditCommand(const string& arguments, 
												 smatch& matchingResults) {
	EditSubType returnedSubType;

	if (regex_match(arguments, matchingResults, regex(regexArgumentEditEnd))) {
		returnedSubType = EDIT_END;
	}else if (regex_match(arguments, matchingResults, regex(regexArgumentEditStart))) {
		returnedSubType = EDIT_START;
	}else if (regex_match(arguments, matchingResults, regex(regexArgumentEditRename))) {
		returnedSubType = EDIT_NAME;
	}else {
		returnedSubType = INVALID_SUBTYPE;
	}
		
	return returnedSubType;
}

void ParserEdit::makeParsedCommandEditErrorSyntax(PARSED_COMMAND& parsedCommand, 
											  smatch& matchingArguments) {
	parsedCommand.commandType = INVALID;
	parsedCommand.errorType = parsedCommand.ERROR_FORMAT_EDIT;
}

void ParserEdit::makeParsedCommandEditStart(PARSED_COMMAND& parsedCommand, 
											  smatch& matchingArguments) {
	try {
		parsedCommand.editTaskID = 
			getTaskIDFromArgument(matchingArguments[ARGUMENT_POS_EDIT_TASKID]);
	}catch (range_error&) {
		parsedCommand.commandType = INVALID;
		parsedCommand.errorType = parsedCommand.ERROR_INVALID_TASK_INDEX;
	}
	// Extract Date/Time and fill up PARSED_COMMAND struct
	ptime parsedDateTime = 
		parseDateTime(matchingArguments[ARGUMENT_POS_EDIT_NEW_START]);
	time_duration parsedTime = 
		parseTime(matchingArguments[ARGUMENT_POS_EDIT_NEW_START]);

	if (!parsedTime.is_not_a_date_time()) {	 // check if user only entered time
		parsedCommand.commandType = EDIT_START_TIME;
		parsedCommand.editStartTime = parsedTime;
	}else if (!parsedDateTime.is_not_a_date_time()) {// check if user entered date+time
		parsedCommand.commandType = EDIT_START_DATETIME;
		parsedCommand.editStartDateTime = parsedDateTime;
	}else {										
		parsedCommand.commandType = INVALID;
		parsedCommand.errorType = parsedCommand.ERROR_FORMAT_DATE;
	}	
}

void ParserEdit::makeParsedCommandEditEnd(PARSED_COMMAND& parsedCommand, 
											  smatch& matchingArguments) {
	try {
		parsedCommand.editTaskID = 
			getTaskIDFromArgument(matchingArguments[ARGUMENT_POS_EDIT_TASKID]);
	}catch (range_error&) {
		parsedCommand.commandType = INVALID;
		parsedCommand.errorType = parsedCommand.ERROR_INVALID_TASK_INDEX;
	}

	// Extract Date/Time and fill up PARSED_COMMAND struct
	ptime parsedDateTime = 
		parseDateTime(matchingArguments[ARGUMENT_POS_EDIT_NEW_END]);
	time_duration parsedTime = 
		parseTime(matchingArguments[ARGUMENT_POS_EDIT_NEW_END]);

	if (!parsedTime.is_not_a_date_time()) {			// check if user only entered time
		parsedCommand.commandType = EDIT_END_TIME;
		parsedCommand.editEndTime = parsedTime;
	}else if (!parsedDateTime.is_not_a_date_time()) {// check if user entered date+time
		parsedCommand.commandType = EDIT_END_DATETIME;
		parsedCommand.editEndDateTime = parsedDateTime;
	}else {										
		parsedCommand.commandType = INVALID;
		parsedCommand.errorType = parsedCommand.ERROR_FORMAT_DATE;
	}
}

void ParserEdit::makeParsedCommandEditRename(PARSED_COMMAND& parsedCommand, 
											  smatch& matchingArguments) {

	parsedCommand.commandType = EDIT_RENAME;
	try {
		parsedCommand.editTaskID = 
			getTaskIDFromArgument(matchingArguments[ARGUMENT_POS_EDIT_TASKID]);
	}catch (range_error&) {
		parsedCommand.commandType = INVALID;
		parsedCommand.errorType = parsedCommand.ERROR_INVALID_TASK_INDEX;
	}

	parsedCommand.editTaskName = matchingArguments[ARGUMENT_POS_EDIT_NEW_NAME];
}
